using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

namespace XmlObjects
{


  /// <summary>
  /// The TypeHelper collects all the information on a type, including the 
  ///   defined fields, attribute information (parent, etc), parse method,
  ///    etc.
  /// The TypeHelper is the main support for the XmlObjects library.
  /// Creating a TypeHelper implies verifying recursively all the referenced
  ///    types: new TypeHelpers are created for each type.
  /// </summary>
  class TypeHelper
  {


    /// <summary>
    /// Builds a new type helper. 
    /// This method is only called by the TypeHelperHandler
    /// </summary>
    /// <param name="xmlType">The type associated to the .Net class 
    /// with Xml representation</param>
    internal TypeHelper(Type xmlType)
    {
      this.xmlType = xmlType;
    }


    /// <summary>
    /// Method to complete the construction of the TypeHelper. This spliting is necesary 
    ///   in case that a type, or any type referenced by it, references itself. 
    /// If that happens, the Build method would try, eventually, to build again 
    ///   a TypeHelper instance,resulting in a stack overflow error (see the 
    ///   typeHelperHandler.CreateInstance method)
    /// This method is only called by the TypeHelperHandler
    /// </summary>
    /// <param name="typeHelperHandler"></param>
    internal void Build(TypeHelperHandler factory)
    {
      if (xmlType.BaseType == null)
        parentTypeHelper = null;
      else
        parentTypeHelper = factory.CreateInstance(xmlType.BaseType);

      //Get the type attribute for this type
      XmlTypeAttribute att = getTypeAttribute(factory);

      typeAttributeName = GetTypeAttributeName(att, factory);

      if (xmlType.IsPrimitive || xmlType.IsEnum)
      {
        parseMethod = getParseMethod();
        isSimpleType = true;
      }
      else if (xmlType == typeof(string))
      {
        isSimpleType = true;
      }
      else
      {
        //get now the defined fields. The fields must be defined in the same type, it is not
        // valid to refer to fields above in the hierarchy
        //If the type does not define itself a childs field, the default one, stored in the 
        //  typeHelperHandler, is used.
        childsField = getSpecialFileHandler(att.ChildsField, att.InnerTextFieldDefined,
          factory.ChildsField, factory);
        parentField = getSpecialFileHandler(att.ParentField, att.ParentFieldDefined,
          factory.ParentField, factory);
        innerTextField = getSpecialFileHandler(att.InnerTextField, att.InnerTextFieldDefined,
          factory.InnerTextField, factory);
        genericElementField = getSpecialFileHandler(att.GenericElementField, att.GenericElementDefined,
          factory.GenericElementField, factory);
        writeStartedMethod = getSpecialMethodInfo(att.XmlWritePrepareMethod, att.WriteStartedMethodDefined,
          factory.XmlWritePrepareMethod, factory);
        readCompletedMethod = getSpecialMethodInfo(att.XmlReadCompletedMethod, att.ReadCompletedMethodDefined,
          factory.XmlReadCompletedMethod, factory);
        writeCompletedMethod = getSpecialMethodInfo(att.XmlWriteCompletedMethod, att.WriteCompletedMethodDefined,
          factory.XmlWriteCompletedMethod, factory);

        //Get now information on every field defined in THIS type (forget hierarchy)
        inspectFields(!att.ExplicitFields, att.AttributeFields, att.ProvidedFieldsSupport, factory);

        //the parse method can be required if no inner text field is defined, and there are
        //no attributes or elements
        if (!DefineInnerTextField && !DefineFields)
          parseMethod = getParseMethod();

      }
    }



    /// <summary>
    /// Checks all the public instance fields
    /// </summary>
    /// <param name="implicitTypes"></param>
    /// <param name="mapNonArraysToAttributes"></param>
    /// <param name="typeHelperHandler"></param>
    /// <param name="providedFieldsSupport"></param>
    private void inspectFields(bool implicitTypes, bool attributeFields, bool providedFieldsSupport,
      TypeHelperHandler factory)
    {
      foreach (FieldInfo fi in xmlType.GetFields(BindingFlags.Public | BindingFlags.Instance))
      {
        if (fi.DeclaringType == xmlType)
          inspectField(fi, implicitTypes, attributeFields, providedFieldsSupport, factory);
      }
    }



    /// <summary>
    /// Check one specific public instance field
    /// </summary>
    /// <param name="fi"></param>
    /// <param name="implicitTypes"></param>
    /// <param name="mapNonArraysToAttributes"></param>
    /// <param name="typeHelperHandler"></param>
    /// <param name="providedFieldsSupport"></param>
    private void inspectField(FieldInfo fi, bool implicitTypes, bool attributeFields,
      bool providedFieldsSupport, TypeHelperHandler factory)
    {
      bool explicitField = false; //will be set to true if the field defines a XmlField attribute
      XmlFieldAttribute.XmlFieldType fieldType = XmlFieldAttribute.XmlFieldType.NotDefined;
      String xmlName = fi.Name;

      foreach (XmlFieldAttribute att in fi.GetCustomAttributes(typeof(XmlFieldAttribute), false))
      {
        if (att.Name != null) xmlName = att.Name;
        explicitField = true;
        fieldType = att.GetFieldType();
        //if the type is an array, fieldType cannot be set
        if (fieldType == XmlFieldAttribute.XmlFieldType.Attribute && fi.FieldType.IsArray)
          throw new XmlObjectsException(XmlObjectsError.AsElementUnsetOnArray, Name, fi.Name);
      }

      if (isExplicitField(fi))
      {
        //if fi is an ExplicitField (provided in XmlTypeAttribute) and it has a XmlFieldAttribute,
        //raise an error (the user tries to use it as special and attribute/element)
        if (explicitField)
          throw new XmlObjectsException(XmlObjectsError.ExplicitOnSpecialField, Name, fi.Name);
      }
      //do not handle the field if UseExplicitlyXmlFieldsAttribute is set and the field
      // does not define a XmlField attribute, or if the field defines a XmlNoField attribute
      //If a field defines both XmlField and XmlNoField, the current behaviour is to dismiss the
      // XmlNoField.
      else if (explicitField ||
        (implicitTypes && fi.GetCustomAttributes(typeof(XmlNoFieldAttribute), false).Length == 0))
      {
        if (explicitField || !(providedFieldsSupport && associateProvidedSupportField(fi)))
          registerValidField(fi, xmlName.ToLower(), fieldType, attributeFields, factory);
      }
    }


    /// <summary>
    /// Associates the given fieldInfo to a FieldHandler, if it follows the stablished rules:
    ///   the associated FieldHandler is a boolean attribute, already defined, and this
    ///     field is named after that attribute with termination 'AttributeProvided'
    ///    or the associated FieldHandler is a boolean element, already defined, and this
    ///     field is named after that element with termination 'ElementProvided'
    /// </summary>
    /// <param name="fi"></param>
    /// <returns></returns>
    private bool associateProvidedSupportField(FieldInfo fi)
    {
      FieldHandler assoc=null;
      if (fi.FieldType==typeof(bool) && 
        (getAssocField(fi, attributes, "AttributeProvided", ref assoc )
        || getAssocField(fi, elements, "ElementProvided", ref assoc)))
      {
        if (!assoc.IsArray())
        {
          assoc.ProvidedSupportField = fi;
          return true;
        }
      }
      return false;
    }



    /// <summary>
    /// Returns the AssociationField for a ProvidedSupport field
    /// </summary>
    /// <param name="fi">The tempative provided support field</param>
    /// <param name="map">The attributes or elements collection</param>
    /// <param name="termination">The expected name termination</param>
    /// <param name="assoc">Output FieldHandler -if found-</param>
    /// <returns>True if it has an association field</returns>
    private bool getAssocField(FieldInfo fi, Dictionary<string, FieldHandler> map, 
      string termination, ref FieldHandler assoc)
    {
      if (fi.Name.EndsWith(termination))
      {
        return map.TryGetValue(fi.Name.Substring(0, fi.Name.Length - termination.Length).ToLower(),
          out assoc);
      }
      return false;
    }



    /// <summary>
    /// Handles one specific public instance field, that will have Xml representation
    /// The name cannot be used by other attribute or field
    /// </summary>
    /// <param name="fi"></param>
    /// <param name="xmlName"></param>
    /// <param name="fieldType"></param>
    /// <param name="attributeFields"></param>
    /// <param name="typeHelperHandler"></param>
    private void registerValidField(FieldInfo fi, string xmlName,
      XmlFieldAttribute.XmlFieldType fieldType, bool attributeFields, TypeHelperHandler factory)
    {
      FieldHandler fieldHandler = new FieldHandler(fi, factory, fieldType, attributeFields);

      if (fieldHandler.IsAttribute())
      {
        if (xmlName == typeAttributeName)
          throw new XmlObjectsException(XmlObjectsError.FieldWithTypeAttributeName, Name, xmlName);

        if (attributes.ContainsKey(xmlName))
          throw new XmlObjectsException(XmlObjectsError.MultipleFieldsWithSameName, Name, xmlName);

        attributes[xmlName] = new FieldHandler(fi, factory, fieldType, attributeFields);
      }
      else
      {
        if (elements.ContainsKey(xmlName))
          throw new XmlObjectsException(XmlObjectsError.MultipleFieldsWithSameName, Name, xmlName);

        elements[xmlName] = new FieldHandler(fi, factory, fieldType, attributeFields);
      }
    }



    private bool isExplicitField(FieldInfo fi)
    {
      return isExplicitField(fi, innerTextField) || isExplicitField(fi, parentField) || 
        isExplicitField(fi, childsField) || isExplicitField(fi, genericElementField);
    }


    private bool isExplicitField(FieldInfo fi, FieldHandler compare)
    {
      return (compare != null) && (fi == compare.FieldInfo);
    }



    /// <summary>
    /// Returns the TypeAttributeName for the current type
    /// </summary>
    /// <param name="defined"></param>
    /// <param name="generic"></param>
    /// <returns></returns>
    public string GetTypeAttributeName(XmlTypeAttribute defined, XmlTypeAttribute generic)
    {
      if (defined.TypeAttributeDefined) return defined.TypeAttribute;
      string byParent = parentTypeHelper == null? null : parentTypeHelper.GetTypeAttributeName();
      return byParent == null ? generic.TypeAttribute : byParent;
    }



    /// <summary>
    /// Returns the static Parse method, if defined for the current class (not on upper classes)
    /// </summary>
    private MethodInfo getParseMethod()
    {
      return xmlType.GetMethod("Parse", BindingFlags.Public | BindingFlags.Static,
        null, parseTypes, null);
    }



    /// <summary>
    /// Returns the type attribute associated to the type, or a default one if none defined.
    /// The attribute must be defined for the given type, not in the hierarchy.
    /// </summary>
    /// <returns></returns>
    private XmlTypeAttribute getTypeAttribute(TypeHelperHandler factory)
    {
      XmlTypeAttribute[] xmlTypeAtt = (XmlTypeAttribute[])
        xmlType.GetCustomAttributes(typeof(XmlTypeAttribute), false);

      if (xmlTypeAtt.Length == 0) return factory.XmlTypeAttributeWithoutFields;

      XmlTypeAttribute ret = xmlTypeAtt[0];
      if (!ret.ExplicitFieldsDefined) ret.ExplicitFields = factory.ExplicitFields;
      if (!ret.AttributeFieldsDefined) ret.AttributeFields = factory.AttributeFields;
      if (!ret.ProvidedFieldsSupportDefined) ret.ProvidedFieldsSupport = factory.ProvidedFieldsSupport;
      return ret;
    }





    /// <summary>
    /// Gets the given field, if specified, looking for it in the current object
    /// </summary>
    /// <param name="fieldName"></param>
    /// <returns></returns>
    private FieldHandler getSpecialFileHandler(string fieldName, bool fieldNameExplicitleSet,
      string defaultFieldName, TypeHelperHandler factory)
    {
      bool optional = false;
      if (fieldName == null)
      {
        if (fieldNameExplicitleSet || (defaultFieldName == null)) return null;
        fieldName = defaultFieldName;
        optional = true;
      }
      FieldInfo ret = xmlType.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance);
      //GetField can return fields in the hierarchy, even if FlattenHierarchy is not specified!
      if ((ret == null) || (ret.DeclaringType != xmlType))
      {
        if (optional) return null;
        throw new XmlObjectsException(XmlObjectsError.FieldDoesNotExist, Name, fieldName);
      }
      return new FieldHandler(ret, factory, XmlFieldAttribute.XmlFieldType.NotDefined, true);
    }



    /// <summary>
    /// Gets the given field, if specified, looking for it in the current object
    /// </summary>
    /// <param name="fieldName"></param>
    /// <returns></returns>
    private MethodInfo getSpecialMethodInfo(string methodName, bool methodNameExplicitleSet,
      string defaultMethodName, TypeHelperHandler factory)
    {
      bool optional = false;
      if (methodName == null)
      {
        if (methodNameExplicitleSet || (defaultMethodName == null)) return null;
        methodName = defaultMethodName;
        optional = true;
      }
      MethodInfo ret = xmlType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance,
        null, System.Type.EmptyTypes, emptyModifiers);
      if ((ret == null) || (ret.DeclaringType != xmlType))
      {
        if (optional) return null;
        throw new XmlObjectsException(XmlObjectsError.MethodDoesNotExist, Name, methodName);
      }
      return ret;
    }



    /// <summary>
    /// Creates an instance of the given type, and associates the text, 
    ///   using the innerTextField or the parse method
    /// </summary>
    /// <param name="innerText"></param>
    /// <returns></returns>
    public Object CreateInstance(string innerText)
    {
      object ret;
      //Note that parseMethod is not not-null just because a type defines the Parse method
      //it is only found for simple types (and enumerations and strings do not have it)
      //  and for not simple types that do not define any fields.
      if (parseMethod != null)
      {
        try
        {
          ret = parseMethod.Invoke(null, new object[] { innerText });
        }
        catch (TargetInvocationException)
        {
          throw new XmlObjectsException(XmlObjectsError.InvalidParsingString, Name, innerText);
        }
        //ensure that the type is as expected
        if (!xmlType.IsInstanceOfType(ret))
          throw new XmlObjectsException(XmlObjectsError.InvalidParseMethod, Name);
      }
      else if (isSimpleType)
      {
        if (xmlType.IsEnum)
          ret = Enum.Parse(xmlType, innerText);
        else
          ret = innerText; //can only be a string!
      }
      else
      {
        if (constructor == null)
        {
          //perhaps this is the first time the constructor is accessed
          constructor = xmlType.GetConstructor(System.Type.EmptyTypes);
          if (constructor == null)
            throw new XmlObjectsException(XmlObjectsError.NoEmptyConstructor, Name);
        }

        ret = constructor.Invoke(emptyParameters);

        if (innerText != "")
        {
          if (!DefineInnerTextField)
            throw new XmlObjectsException(XmlObjectsError.NoInnerText, Name);
          SetInnerText(ret, innerText);
        }
      }
      return ret;
    }



    /// <summary>
    /// Returns the TypeHelper associated to the field with the given name
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public FieldHandler GetElementHandler(string name)
    {
      return GetElementHandlerLC(name.ToLower(), Name);
    }



    /// <summary>
    /// Returns the FieldHandler associated to the attribute with the given name
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public FieldHandler GetAttributeHandler(string name)
    {
      return GetAttributeHandlerLC(name.ToLower(), Name);
    }



    /// <summary>
    /// Retuns the type represented by this TypeHelper
    /// </summary>
    public string Name
    {
      get { return xmlType.Name; }
    }


    /// <summary>
    /// Returns true if the type is considered simple (primitives, strings, enumerations)
    /// </summary>
    public bool SimpleType
    {
      get { return isSimpleType; }
    }


    /// <summary>
    /// Converts value, an instance of the underlying type, to a string.
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public string ConvertToString(object value)
    {
      //Use the ToString method if the type is simple or defines a Parse method
      // and has no attributes or fields
      if ((isSimpleType || ((parseMethod != null) && !DefineFields)))
        return value.ToString();
      //Now, the only value of the given string can be given by the inner text, if
      // defined. If not (and not defined in parent classes), the value is just the
      // empty string
      return GetInnerText(value);
    }



    /// <summary>
    /// Returns the TypeAttribute, if needed, for an object represented by
    ///  this TypeHelper, when the declaredType is as specified.
    /// If no type is required (or not supported by the given class), null
    ///  is returned.
    /// </summary>
    /// <param name="declaredType"></param>
    /// <returns></returns>
    public XmlAttribute GetTypeAttribute(Type declaredType, TypeHelperHandler handler)
    {
      if (typeAttributeName != null)
      {
        string value = handler.GetTypeAttributeValue(xmlType, declaredType);
        if (value != null)
          return new XmlAttribute(typeAttributeName, value);
      }
      return null;
    }



    /// <summary>
    /// Iterator through all the elements in the passed instance (which must have
    ///   the type of the associated type).
    /// The elements are returned as pairs name / object, and the object can never
    ///   be null.
    /// If the type defined any childs field, the elements are returned in the
    ///   order given by this field.
    /// This assumes that all the elements of the instance are included in the
    ///   childs array. If that would not be the case, an exception is raised
    /// </summary>
    /// <param name="instance"></param>
    /// <returns></returns>
    public IEnumerable<XmlElement> GetElements(object instance)
    {
      FieldHandler childsHandler = GetXmlChildsField();
      if (childsHandler == null) return BaseGetElements(instance);
      return getElementsSortedByChilds(instance, childsHandler);
    }



    /// <summary>
    /// Returns the elements sorted by childs. See GetElements.
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="childsField"></param>
    /// <returns></returns>
    private IEnumerable<XmlElement> getElementsSortedByChilds(object instance,
      FieldHandler childsField)
    {
      Dictionary<object, Stack<XmlElement>> repeatedElements;
      Dictionary<object, XmlElement> allElements = getAllElements(instance, out repeatedElements);
      foreach (object value in childsField.GetValues(instance))
      {
        XmlElement element;
        Stack<XmlElement> assoc;
        if (repeatedElements.TryGetValue(value, out assoc))
        {
          element = assoc.Pop();
          if (assoc.Count == 0) repeatedElements.Remove(value);
        }
        else if (allElements.TryGetValue(value, out element))
        {
          allElements.Remove(value);
        }
        else
        {
          throw new XmlObjectsException(XmlObjectsError.UnmatchingChildsField, Name);
        }
        yield return element;
      }
      if (allElements.Count > 0 || repeatedElements.Count > 0)
        throw new XmlObjectsException(XmlObjectsError.UnmatchingChildsField, Name);
    }


    /// <summary>
    /// Returns all the elements for the current type, as a dictionary object -> element name
    /// As there can be repeated elements (for example, two integers with the same value), it
    ///   is needed to foresee this repetition; therefore, it is also returned an additional
    ///   dictionary that maps for each object the additional keys found
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="childsField"></param>
    /// <returns></returns>
    private Dictionary<object, XmlElement> getAllElements(object instance,
      out Dictionary<object, Stack<XmlElement>> repeatedElements)
    {
      Dictionary<object, XmlElement> allElements = new Dictionary<object, XmlElement>();
      repeatedElements = new Dictionary<object, Stack<XmlElement>>();
      foreach (XmlElement element in BaseGetElements(instance))
      {
        try
        {
          allElements.Add(element.Value, element);
        }
        catch (ArgumentException)
        {
          //kv.Value is repeated. This can be an error, or there could be two childs with 
          //  the same values (for example two integers). We keep all the associated keys 
          //  (this should be not a normal case, no need to use the Stack for all)
          Stack<XmlElement> assoc;
          if (!repeatedElements.TryGetValue(element.Value, out assoc))
          {
            assoc = new Stack<XmlElement>();
            repeatedElements[element.Value] = assoc;
          }
          assoc.Push(element);
        }
      }
      return allElements;
    }



    /// <summary>
    /// Returns thetype associated to the helper
    /// </summary>
    public Type XmlType { get { return xmlType; } }




    /// <summary>
    /// Returns all the elements associated to the given instance, as pairs
    ///   element name, object; the values are returned in no particular order. 
    /// Null objects are never returned
    /// </summary>
    /// <param name="instance">The object to inspect</param>
    /// <returns></returns>
    private IEnumerable<XmlElement> BaseGetElements(object instance)
    {
      if (parentTypeHelper != null)
        foreach (XmlElement element in parentTypeHelper.BaseGetElements(instance))
          yield return element;
      foreach (KeyValuePair<string, FieldHandler> kv in elements)
        if (kv.Value.IsSpecified(instance))
          foreach (object obj in kv.Value.GetValues(instance))
            yield return new XmlElement(kv.Key, obj, kv.Value.TypeHelper.xmlType);
      if (genericElementField!=null)
        foreach (object obj in genericElementField.GetValues(instance))
          yield return new XmlElement(obj.GetType().Name.ToLower(), obj, genericElementField.TypeHelper.xmlType);
    }




    /// <summary>
    /// Returns all the attributes associated to the given instance as XmlAttributes
    ///   attribute name, attribute value.
    /// Null attributes or empty attributes (whose string representation is empty),
    ///   are not returned
    /// </summary>
    /// <param name="instance">The object to inspect</param>
    /// <returns></returns>
    public IEnumerable<XmlAttribute> GetAttributes(object instance)
    {
      if (parentTypeHelper != null)
        foreach (XmlAttribute att in parentTypeHelper.GetAttributes(instance))
          yield return att;
      foreach (KeyValuePair<string, FieldHandler> kv in attributes)
        if (kv.Value.IsSpecified(instance))
          yield return new XmlAttribute(kv.Key, kv.Value.GetValue(instance));
    }



    /// <summary>
    /// Returns all the FieldHandlers associated to element variables of the current
    ///   type (and parent types)
    /// </summary>
    /// <returns></returns>
    public IEnumerable<FieldHandler> ElementFieldHandlers
    {
      get
      {
        foreach (KeyValuePair<string, FieldHandler> kv in elements)
          yield return kv.Value;
        if (parentTypeHelper != null)
          foreach (FieldHandler fh in parentTypeHelper.ElementFieldHandlers)
            yield return fh;
        if (genericElementField != null)
          yield return genericElementField;
      }
    }



    /// <summary>
    /// Returns the FieldHandler associated to the attribute
    ///   with the given name, specified in lowercase
    /// An exception is raised if the field does not exists
    /// </summary>
    /// <param name="lowerName">name of the field, in lower case</param>
    /// <param name="typeName">name of the current type</param>
    /// <returns></returns>
    private FieldHandler GetAttributeHandlerLC(string lowerName, string typeName)
    {
      FieldHandler ret;
      if (attributes.TryGetValue(lowerName, out ret)) return ret;
      if (parentTypeHelper!=null) return parentTypeHelper.GetAttributeHandlerLC(lowerName, typeName);
      throw new XmlObjectsException(XmlObjectsError.AttributeDoesNotExist, typeName, lowerName);
    }



    /// <summary>
    /// Returns the FieldHandler associated to the attribute
    ///   with the given name, specified in lowercase
    /// An exception is raised if the field does not exists
    /// </summary>
    /// <param name="lowerName">name of the field, in lower case</param>
    /// <param name="typeName">name of the current type</param>
    /// <returns></returns>
    private  FieldHandler GetElementHandlerLC(string lowerName, string typeName)
    {
      FieldHandler ret;
      if (elements.TryGetValue(lowerName, out ret)) return ret;
      if (genericElementField != null) return genericElementField;
      if (parentTypeHelper!=null) return parentTypeHelper.GetElementHandlerLC(lowerName, typeName);
      throw new XmlObjectsException(XmlObjectsError.ElementDoesNotExist, typeName, lowerName);
    }



    /// <summary>
    /// Sets the value of the childs field on the given object,
    ///   which must match the type associated to the TypeHelper
    /// If the associated type has no childs field, no error is raised.
    /// In any case, it is propagated to TypeHelperInterfaces in the
    ///   hierarchy, so if there is more than one type in the hierarchy
    ///   defining this field, the information is properly populated.
    /// </summary>
    /// <param name="instance">The instance to update</param>
    /// <param name="childs">ArrayList of objects</param>
    public void SetXmlChilds(object instance, ArrayList childs)
    {
      if (childsField != null) childsField.SetValue(instance, childs);
      if (parentTypeHelper!=null) parentTypeHelper.SetXmlChilds(instance, childs);
    }



    /// <summary>
    /// Sets the value of the parent field on the given object,
    ///   which must match the type associated to the TypeHelper
    /// If the associated type has no parent field, no error is raised.
    /// In any case, it is propagated to TypeHelperInterfaces in the
    ///   hierarchy, so if there is more than one type in the hierarchy
    ///   defining this field, the information is properly populated.
    /// </summary>
    /// <param name="instance">The instance to update</param>
    /// <param name="parent">The Xml parent of the given instance</param>
    public void SetXmlParent(object instance, object parent)
    {
      if (parentField != null) parentField.SetValue(instance, parent);
      if (parentTypeHelper != null) parentTypeHelper.SetXmlParent(instance, parent);
    }



    /// <summary>
    /// Sets the value of the inner text on the given object,
    ///   which must match the type associated to the TypeHelper
    /// If the associated type has no inner text field, no error is raised.
    /// In any case, it is propagated to TypeHelperInterfaces in the
    ///   hierarchy, so if there is more than one type in the hierarchy
    ///   defining this field, the information is properly populated.
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="text"></param>
    public void SetInnerText(object instance, string text)
    {
      if (innerTextField != null) innerTextField.SetValue(instance, text);
      if (parentTypeHelper != null) parentTypeHelper.SetInnerText(instance, text);
    }



    /// <summary>
    /// Invokes, if any, the method specified as XmlReadCompleted
    /// If the associated type has no completed method, no error is raised.
    /// In any case, it is propagated to TypeHelperInterfaces in the
    ///   hierarchy.
    /// </summary>
    /// <param name="instance"></param>
    public void BuildCompleted(object instance)
    {
      if (readCompletedMethod != null) readCompletedMethod.Invoke(instance, emptyParameters);
      if (parentTypeHelper != null) parentTypeHelper.BuildCompleted(instance);
    }



    /// <summary>
    /// Invokes, if any, the method specified as XmlWriteStarted
    /// If the associated type has no completed method, no error is raised.
    /// In any case, it is propagated to TypeHelperInterfaces in the
    ///   hierarchy.
    /// </summary>
    /// <param name="instance"></param>
    public void WriteStarted(object instance)
    {
      if (writeStartedMethod != null) writeStartedMethod.Invoke(instance, emptyParameters);
      if (parentTypeHelper != null) parentTypeHelper.WriteStarted(instance);
    }



    /// <summary>
    /// Invokes, if any, the method specified as XmlWriteCompleted
    /// If the associated type has no completed method, no error is raised.
    /// In any case, it is propagated to TypeHelperInterfaces in the
    ///   hierarchy.
    /// </summary>
    /// <param name="instance"></param>
    public void WriteCompleted(object instance)
    {
      if (writeCompletedMethod != null) writeCompletedMethod.Invoke(instance, emptyParameters);
      if (parentTypeHelper != null) parentTypeHelper.WriteCompleted(instance);
    }



    /// <summary>
    /// Returns true if the object has an associated inner text.
    /// This method is called from the constructor of the TypeHelper!
    /// </summary>
    /// <returns></returns>
    public bool DefineInnerTextField
    {
      get { return (innerTextField != null) || (parentTypeHelper!=null && parentTypeHelper.DefineInnerTextField); }
    }



    /// <summary>
    /// Property to determine if the type defines any field
    /// </summary>
    /// <returns></returns>
    public bool DefineFields
    {
      get { return (attributes.Count > 0) || (elements.Count>0) || (parentTypeHelper!=null && parentTypeHelper.DefineFields); }
    }



    /// <summary>
    /// Returns the inner text's value, if the inner text is defined.
    /// Note that a type could define more than one inner text if
    ///   more than one element in its hierarchy defines it. In this
    ///   case, the inner text is associated to the more specialized
    ///   class
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="text"></param>
    public string GetInnerText(object instance)
    {
      FieldHandler info = InnerTextField;
      return info == null ? "" : info.GetValue(instance);
    }


    /// <summary>
    /// Returns, if any, the inner text field
    /// </summary>
    public FieldHandler InnerTextField
    {
      get
      {
        if (innerTextField != null) return innerTextField;
        return parentTypeHelper == null ? null : parentTypeHelper.innerTextField;
      }
    }


    public TypeHelper ParentTypeHelper
    {
      get
      {
        return parentTypeHelper;
      }
    }

    /// <summary>
    /// Returns the childs field
    /// Note that a type could define more than one childs text if
    ///   more than one element in its hierarchy defines it. In this
    ///   case, the returned childs field is the field associated to 
    ///   the more specialized class
    /// </summary>
    public FieldHandler GetXmlChildsField()
    {
      if (childsField != null) return childsField;
      return parentTypeHelper==null? null : parentTypeHelper.GetXmlChildsField();
    }


    /// <summary>
    /// Returns the type attribute name, or null if it is not specified
    /// Note that a type could define more than one type attribute if
    ///   more than one element in its hierarchy defines it. In this
    ///   case, the returned value is the attribute associated to 
    ///   the more specialized class
    /// </summary>
    public string GetTypeAttributeName()
    {
      return typeAttributeName;
    }

    /// <summary>
    /// Returns, the names of all the elements (inherited or not)
    /// </summary>
    public List<String> ElementNames
    {
      get
      {
        return fillElementNames(new List<string>());
      }
    }

    /// <summary>
    /// Returns the names of the supported elements
    /// </summary>
    private List<String> fillElementNames(List<String> where)
    {
      if (parentTypeHelper != null)
        parentTypeHelper.fillElementNames(where);
      foreach (String key in elements.Keys)
        where.Add(key);
      return where;
    }

    
    /// <summary>
    /// Returns, the names of all the attributes (inherited or not)
    /// </summary>
    public List<String> AttributeNames
    {
      get
      {
        return fillAttributeNames(new List<string>());
      }
    }

    /// <summary>
    /// Returns the names of the supported attributes
    /// </summary>
    private List<String> fillAttributeNames(List<String> where)
    {
      if (parentTypeHelper != null)
        parentTypeHelper.fillAttributeNames(where);
      foreach (String key in attributes.Keys)
        where.Add(key);
      return where;
    }



    /// <summary>
    /// Set to true if the type is considered simple (primitives, string, enumerations)
    /// </summary>
    private bool isSimpleType;

    /// <summary>
    /// The TypeHelper associated to the parent type. It is never null
    /// </summary>
    private TypeHelper parentTypeHelper;

    /// <summary>
    /// The represented type
    /// </summary>
    private Type xmlType;

    /// <summary>
    /// Each XML attribute and/or element is mapped to a field in the C# class, 
    ///    which has a FieldInfo instance (reflection type) associated.
    /// This dictionary maps those XML attribute/element names to the FieldHandler
    ///    instances that embed the associated FieldInfo instances.
    /// </summary>
    private Dictionary<string, FieldHandler> attributes = new Dictionary<string, FieldHandler>();
    private Dictionary<string, FieldHandler> elements = new Dictionary<string, FieldHandler>();
    private FieldHandler innerTextField, childsField, parentField, genericElementField;
    private MethodInfo parseMethod, readCompletedMethod, writeStartedMethod, writeCompletedMethod;
    private ConstructorInfo constructor;
    private string typeAttributeName;

    private static object[] emptyParameters = new object[0];
    private static Type[] parseTypes = new Type[] { typeof(string) };
    private static ParameterModifier[] emptyModifiers = new ParameterModifier[0];

  }
}
